package top.maof.book.aspect;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import top.maof.book.annotation.His;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Aspect
@Component
@Slf4j
public class LogAspect {


    @Pointcut("execution(public * top.maof.book.controller..*.*(..))")
    public void log() {
    }

    @Pointcut("@annotation(top.maof.book.annotation.His)")
    public void history() {

    }

    /**
     * 拦截@His注解，写入历史表
     */
    @AfterReturning(value = "history()", returning = "result")
    public void before(JoinPoint joinPoint, Object result) throws Throwable {
        log.info("@History before");
        //获取连接点的方法签名对象
        Signature signature = joinPoint.getSignature();

        //获取方法
        Method method = ((MethodSignature) signature).getMethod();

        // 获取注解
        His his = method.getAnnotation(His.class);

        if (his != null) {
            // 创建解析器
            SpelExpressionParser parser = new SpelExpressionParser();
            // 获取表达式
            Expression expression = parser.parseExpression(his.token());
            // 设置解析上下文(有哪些占位符，以及每种占位符的值)
            EvaluationContext context = new StandardEvaluationContext();
            //获取参数值
            Object[] args = joinPoint.getArgs();
            //获取运行时参数的名称
            DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
            String[] parameterNames = discoverer.getParameterNames(method);
            for (int i = 0; i < parameterNames.length; i++) {
                context.setVariable(parameterNames[i], args[i]);
            }
            //解析,获取替换后的结果
            String bookId = expression.getValue(context).toString();

        }
    }


    @AfterReturning(value = "log()", returning = "result")
    public void afterRunning(JoinPoint jp, Object result) {

        log.info("LogAspect 执行...");

        //原始的HTTP请求和响应的信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        Signature signature = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //获取当前执行的方法
        Method targetMethod = methodSignature.getMethod();

        //获取参数
        Object[] objects = jp.getArgs();
        //获取参数
        Object[] arguments = new Object[objects.length];

        for (int i = 0; i < objects.length; i++) {
            if (objects[i] instanceof ServletRequest || objects[i] instanceof ServletResponse ||
                    objects[i] instanceof MultipartFile) {
                //过滤掉不能序列化的参数
                continue;
            }
            arguments[i] = objects[i];
        }

        StringBuilder sb = new StringBuilder(1024);

        sb.append("\n-------------------------------------------------------------\n");
        sb.append("Controller: ").append(targetMethod.getDeclaringClass().getName()).append("\n");
        sb.append("Method    : ").append(targetMethod.getName()).append("\n");
        sb.append("Params    : ").append(JSON.toJSONString(arguments).toString()).append("\n");
        sb.append("URL       : ").append(request.getRequestURL()).append("\n");
        sb.append(JSON.toJSONString(result));
        sb.append("\n-------------------------------------------------------------\n");
        log.info(sb.toString());
    }


}
